package com.boot.es.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

import org.elasticsearch.index.query.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

public class Utils {

    private static final Logger log = LoggerFactory.getLogger(Utils.class);


    public static void generateQuery(@RequestParam String query,Class clz, BoolQueryBuilder boolQueryBuilder) {
        JSONObject jsonObject = JSON.parseObject(query);
        Set<Map.Entry<String, Object>> set = jsonObject.entrySet();
        for (Map.Entry<String, Object> entry:set) {
            Object val = null;
            try{
                val =  JSON.parse(entry.getValue().toString());
            }catch(JSONException e){
                val = entry.getValue().toString();
            }

            if(val instanceof JSONArray){

                JSONArray jsonAry =  (JSONArray)val;
                Iterator<Object> iter = jsonAry.iterator();
                while(iter.hasNext()){
                    JSONObject parObj = (JSONObject)iter.next();
                    Set<Map.Entry<String, Object>> parObjectSet = parObj.entrySet();
                    for (Map.Entry<String, Object> par:parObjectSet) {
                        Field field = getDeclaredField(entry.getKey(),clz);
                        Object parValue = null;
                        if("java.lang.Integer".equals(field.getType().getTypeName())){
                            RangeQueryBuilder rqb = QueryBuilders.rangeQuery(entry.getKey());
                            if ("lte".equals(par.getKey())) {
                                parValue = new Integer(par.getValue().toString());
                                rqb.lte(parValue);
                                boolQueryBuilder.must(rqb);
                            } else if ("lt".equals(par.getKey())) {
                                parValue = new Integer(par.getValue().toString());
                                rqb.lt(parValue);
                                boolQueryBuilder.must(rqb);
                            } else if ("gte".equals(par.getKey())) {
                                parValue = new Integer(par.getValue().toString());
                                rqb.gte(parValue);
                                boolQueryBuilder.must(rqb);
                            } else if ("gt".equals(par.getKey())) {
                                parValue = new Integer(par.getValue().toString());
                                rqb.gt(parValue);
                                boolQueryBuilder.must(rqb);
                            }else if("like".equals(par.getKey())){
                                throw new RuntimeException("数值类型不支持模糊匹配");
                            }else if("in".equals(par.getKey())){
                                String [] ary = par.getValue().toString().split(",");
                                ArrayList< String> arrayList = new ArrayList<String>(ary.length);
                                Collections.addAll(arrayList,ary);
                                QueryBuilder qb = QueryBuilders.termsQuery(entry.getKey(),arrayList);
                                boolQueryBuilder.must(qb);
                            }else if("neq".equals(par.getKey())){
                                parValue = new Integer(par.getValue().toString());

                                QueryBuilder qb = QueryBuilders.termQuery(entry.getKey(),parValue);
                                boolQueryBuilder.mustNot(qb);
                            }
                        }else if("java.lang.Long".equals(field.getType().getTypeName())) {
                            RangeQueryBuilder rqb = QueryBuilders.rangeQuery(entry.getKey());
                            if ("lte".equals(par.getKey())) {
                                parValue = new Long(par.getValue().toString());
                                rqb.lte(parValue);
                                boolQueryBuilder.must(rqb);
                            } else if ("lt".equals(par.getKey())) {
                                parValue = new Long(par.getValue().toString());
                                rqb.lt(parValue);
                                boolQueryBuilder.must(rqb);
                            } else if ("gte".equals(par.getKey())) {
                                parValue = new Long(par.getValue().toString());
                                rqb.gte(parValue);
                                boolQueryBuilder.must(rqb);
                            } else if ("gt".equals(par.getKey())) {
                                parValue = new Long(par.getValue().toString());
                                rqb.gt(parValue);
                                boolQueryBuilder.must(rqb);
                            }else if("like".equals(par.getKey())){
                                throw new RuntimeException("数值类型不支持模糊匹配");
                            }else if("in".equals(par.getKey())){
                                String [] ary = par.getValue().toString().split(",");
                                ArrayList< String> arrayList = new ArrayList<String>(ary.length);
                                Collections.addAll(arrayList,ary);

                                QueryBuilder qb = QueryBuilders.termsQuery(entry.getKey(),arrayList);
                                boolQueryBuilder.must(qb);
                            }else if("neq".equals(par.getKey())){
                                parValue = new Long(par.getValue().toString());
                                QueryBuilder qb = QueryBuilders.termQuery(entry.getKey(),parValue);
                                boolQueryBuilder.mustNot(qb);
                            }
                        }else{
                            //}else if("java.lang.String".equals(field.getType().getTypeName())){
                            if("lte".equals(par.getKey())){
                                boolQueryBuilder.must(QueryBuilders.rangeQuery(entry.getKey() +".keyword").lte(par.getValue()));
                            }else if("lt".equals(par.getKey())){
                                boolQueryBuilder.must(QueryBuilders.rangeQuery(entry.getKey()+".keyword").lt(par.getValue()));
                            }else if("gte".equals(par.getKey())){
                                boolQueryBuilder.must(QueryBuilders.rangeQuery(entry.getKey()+".keyword").gte(par.getValue()));
                            }else if("gt".equals(par.getKey())){
                                boolQueryBuilder.must(QueryBuilders.rangeQuery(entry.getKey()+".keyword").gt(par.getValue()));
                            }else if("like".equals(par.getKey())){
                            	
                            	System.out.println("===>: "+field.getType().getTypeName());
                            	
                            	if("java.util.List".equals(field.getType().getTypeName())){
                            		QueryBuilder qb = QueryBuilders.wildcardQuery(entry.getKey()+".keyword","*" + par.getValue() + "*");
                                	boolQueryBuilder.must(qb);
                            	}else{
                            		QueryBuilder qb = QueryBuilders.wildcardQuery(entry.getKey()+".keyword","*" + par.getValue() + "*");
                                	boolQueryBuilder.must(qb);
                            	}
                            }else if("rlike".equals(par.getKey())){

                                System.out.println("===>: "+field.getType().getTypeName());

                                if("java.util.List".equals(field.getType().getTypeName())){
                                    QueryBuilder qb = QueryBuilders.wildcardQuery(entry.getKey()+".keyword", par.getValue() + "*");
                                    boolQueryBuilder.must(qb);
                                }else{
                                    QueryBuilder qb = QueryBuilders.wildcardQuery(entry.getKey()+".keyword", par.getValue() + "*");
                                    boolQueryBuilder.must(qb);
                                }
                            }
                            else if("in".equals(par.getKey())){
                                String [] ary = par.getValue().toString().split(",");
                                ArrayList< String> arrayList = new ArrayList<String>(ary.length);
                                Collections.addAll(arrayList,ary);

                                QueryBuilder qb = QueryBuilders.termsQuery(entry.getKey()+".keyword",arrayList);
                                boolQueryBuilder.must(qb);
                            }else if("neq".equals(par.getKey())){
                                QueryBuilder qb = QueryBuilders.termQuery(entry.getKey()+".keyword",par.getValue());
                                boolQueryBuilder.mustNot(qb);
                            }
                        }
                    }
                }
            }else{

                if(entry.getKey().contains(",")){

                    Object value = entry.getValue();

                    String[] keys = entry.getKey().split(",");

                    Map<String, Float> fields = new HashMap<>();

                    /*try{
                        Long.parseLong(value.toString());
                        value = value.toString();
                    }catch(Exception e){
                        value = value.toString() + "*";
                    }*/


                    MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(value);

                    //MultiTermQueryBuilder multiTermQueryBuilder = QueryBuilders.spanMultiTermQueryBuilder(value);
                    for (String key : keys) {
                        Field field = getDeclaredField(key,clz);
                        if("java.lang.String".equals(field.getType().getTypeName())){
                            fields.put(key + ".keyword",1.0F);

                            /*WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery( key+".keyword","*" +entry.getValue().toString().trim()+ "*");


                            SpanMultiTermQueryBuilder spanMultiTermQueryBuilder = QueryBuilders.spanMultiTermQueryBuilder(wildcardQueryBuilder);*/


                            //boolQueryBuilder.must(spanMultiTermQueryBuilder);
                            //boolQueryBuilder.should(wildcardQueryBuilder);
                        }else if("java.lang.Integer".equals(field.getType().getTypeName())){
                            try{
                                value = new Integer(entry.getValue().toString());

                                /*TermQueryBuilder qb = QueryBuilders.termQuery(key,new Integer(entry.getValue().toString()));*/

                                //SpanTermQueryBuilder spanTermQueryBuilder = QueryBuilders.spanTermQuery(key,new Integer(entry.getValue().toString()));
                                /*RangeQueryBuilder rqb = QueryBuilders.rangeQuery(key);
                                rqb.gte(value).lte(value);


                                SpanMultiTermQueryBuilder spanMultiTermQueryBuilder = QueryBuilders.spanMultiTermQueryBuilder(rqb);

                                boolQueryBuilder.must(spanMultiTermQueryBuilder);*/

                                fields.put(key,1.0F);
                            }catch(Exception e){
                                log.info("丢弃字符串匹配Integer类型:" + key);
                            }
                        }else if("java.lang.Long".equals(field.getType().getTypeName())){
                            try{
                                value = new Long(entry.getValue().toString());

                               /* TermQueryBuilder qb = QueryBuilders.termQuery(key,new Long(entry.getValue().toString()));

                                SpanMultiTermQueryBuilder spanMultiTermQueryBuilder = QueryBuilders.spanMultiTermQueryBuilder(qb);*/

                               /* RangeQueryBuilder rqb = QueryBuilders.rangeQuery(key);
                                rqb.gte(value).lte(value);

                                SpanMultiTermQueryBuilder spanMultiTermQueryBuilder = QueryBuilders.spanMultiTermQueryBuilder(rqb);

                                boolQueryBuilder.must(spanMultiTermQueryBuilder);*/
                                //boolQueryBuilder.should(qb);

                                fields.put(key,1.0F);
                            }catch(Exception e){
                                log.info("丢弃字符串匹配Long:"+ key);
                            }
                        }
                    }



                    multiMatchQueryBuilder.fields(fields);
                    boolQueryBuilder.must(multiMatchQueryBuilder);
                    /*
                    multiMatchQueryBuilder.fields(fields);
                    boolQueryBuilder.must(multiMatchQueryBuilder);
                    */

                    //boolQueryBuilder.must(spanMultiTermQueryBuilder);


                    /*Field field = getDeclaredField(key,clz);
                    if(field == null){
                        continue;
                    }
                    if("java.lang.Integer".equals(field.getType().getTypeName())){
                        QueryBuilder qb = QueryBuilders.termQuery(entry.getKey(),new Integer(entry.getValue().toString()));
                        boolQueryBuilder.must(qb);
                    }else if("java.lang.Long".equals(field.getType().getTypeName())){
                        QueryBuilder qb = QueryBuilders.termQuery(entry.getKey(),new Long(entry.getValue().toString()));
                        boolQueryBuilder.must(qb);
                    }else if("java.lang.String".equals(field.getType().getTypeName())){
                        QueryBuilder qb = QueryBuilders.wildcardQuery(entry.getKey()+".keyword","*" +entry.getValue().toString().trim()+ "*");
                        boolQueryBuilder.must(qb);
                    }*/
                }else{
                    Field field = getDeclaredField(entry.getKey(),clz);
                    if(field == null){
                        continue;
                    }
                    if("java.lang.Integer".equals(field.getType().getTypeName())){
                        QueryBuilder qb = QueryBuilders.termQuery(entry.getKey(),new Integer(entry.getValue().toString()));
                        boolQueryBuilder.must(qb);
                    }else if("java.lang.Long".equals(field.getType().getTypeName())){
                        QueryBuilder qb = QueryBuilders.termQuery(entry.getKey(),new Long(entry.getValue().toString()));
                        boolQueryBuilder.must(qb);
                    }else if("java.lang.String".equals(field.getType().getTypeName())){
                        QueryBuilder qb = QueryBuilders.wildcardQuery(entry.getKey()+".keyword","*" +entry.getValue().toString().trim()+ "*");
                        boolQueryBuilder.must(qb);
                    }
                }


            }
        }

        log.info("ES查询：" + boolQueryBuilder.toString());
    }

    public static Pageable getPageable(String order_by, Integer page_size, Integer page_num, Class clz) {
        Sort sort = null;
        if(order_by != null && order_by.trim().length() > 1){
            String[] orders = order_by.split(",");
            for (String order : orders) {
                String [] par = order.split(" ");
                if("desc".equals(par[1].toLowerCase())){
                    if(sort == null){
                        Field field = getDeclaredField(par[0], clz);
                        if("java.lang.String".equals(field.getType().getTypeName())){
                            sort = new Sort(Sort.Direction.DESC,par[0] + ".keyword");
                        }else{
                            sort = new Sort(Sort.Direction.DESC,par[0]);
                        }
                    }else{
                        sort.and(new Sort(Sort.Direction.DESC,par[0]));
                    }
                }else if("asc".equals(par[1].toLowerCase())){

//                    Field field = getDeclaredField(par[0], SkuDraftIndexEntity.class);
//                    if("java.lang.String".equals(field.getType().getTypeName())){
//                        sort = new Sort(Sort.Direction.ASC,par[0] + ".keyword");
//                    }else{
//                        sort.and(new Sort(Sort.Direction.ASC,par[0]));
//                    }
                }
            }
        }else{
            sort = new Sort(Sort.Direction.DESC,"id");
        }

        return new PageRequest(page_num,page_size,sort);
    }

    private static void genQueryByType(BoolQueryBuilder boolQueryBuilder, Map.Entry<String, Object> entry, Map.Entry<String, Object> par, Object parValue) {
        RangeQueryBuilder rqb = QueryBuilders.rangeQuery(entry.getKey());
        if ("lte".equals(par.getKey())) {
            rqb.lte(parValue);
            boolQueryBuilder.must(rqb);
        } else if ("lt".equals(par.getKey())) {
            rqb.lt(parValue);
            boolQueryBuilder.must(rqb);
        } else if ("gte".equals(par.getKey())) {
            rqb.gte(parValue);
            boolQueryBuilder.must(rqb);
        } else if ("gt".equals(par.getKey())) {
            rqb.gt(parValue);
            boolQueryBuilder.must(rqb);
        }else if("like".equals(par.getKey())){
            /*QueryBuilder qb = QueryBuilders.wildcardQuery(entry.getKey(),par.getValue() + "*");
            boolQueryBuilder.must(qb);*/
            /*QueryBuilder qb =  QueryBuilders.prefixQuery(entry.getKey(),par.getValue() + "*");
            boolQueryBuilder.must(qb);*/
            throw new RuntimeException("数值类型不支持模糊匹配");
        }
    }

    /**
     * 循环向上转型, 获取对象的 DeclaredField
     * @param clazz : 子类对象
     * @param fieldName : 父类中的属性名
     * @return 父类中的属性对象
     */

    public static Field getDeclaredField(String fieldName, Class clazz){

        if(fieldName.contains(".")){
            String [] keys = fieldName.split("\\.");
            Field field1 = getDeclaredField(keys[0],clazz);
            if(field1 != null){
                if(field1.getGenericType() instanceof ParameterizedType){
                	ParameterizedType listGenericType = (ParameterizedType) field1.getGenericType();
                	Type[] listActualTypeArguments = listGenericType.getActualTypeArguments();
                	
                	if(listActualTypeArguments.length==1){
                		try{
	                		Type type = listActualTypeArguments[0];
	                		Class field2Class =  Class.forName(type.getTypeName());
	                        Field field2 = getDeclaredField(keys[1],field2Class);
	                        return field2;
                		}catch(Exception e){
                            e.printStackTrace();
                        }
                	}
                	
                }else{
                	Type field2Type = field1.getGenericType();
                    try{
                        Class field2Class =  Class.forName(field2Type.getTypeName());
                        Field field2 = getDeclaredField(keys[1],field2Class);
                        return field2;
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
//                ParameterizedType listGenericType = (ParameterizedType) field1.getGenericType();
//                Type[] listActualTypeArguments = listGenericType.getActualTypeArguments();
//                Type field2Type = listActualTypeArguments[listActualTypeArguments.length-1];
                /*for (int i = 0; i < listActualTypeArguments.length; i++) {
                    System.out.println(listActualTypeArguments[i]);
                }*/
            }
        }

        Field field = null ;

        for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName) ;
                return field ;
            } catch (Exception e) {
                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。
                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }
        return null;
    }
}
